package com.intelij.adapter.source.adapter;

import com.intelij.adapter.actions.UmlAdapterAction;
import com.intelij.adapter.entity.uml.Edge;
import com.intelij.adapter.entity.uml.Node;
import com.intelij.adapter.entity.uml.Point;
import com.intelij.adapter.entity.uml.UmlDocument;
import com.intelij.adapter.eums.ClassType;
import com.intelij.adapter.eums.HeadType;
import com.intelij.adapter.eums.LineType;
import com.intelij.adapter.source.UmlSupportSource;
import com.intelij.adapter.util.Md5Utils;
import org.apache.commons.lang3.StringUtils;
import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Element;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public enum UmlSupportAdapter implements SourceAdapter<UmlSupportSource> {
    UML_SUPPORT;

    public static UmlSupportAdapter getInstance() {
        return UmlSupportAdapter.UML_SUPPORT;
    }

    private Map<String, Node> nodeMap = new HashMap<>();
    private static final int PADDING = 100;

    @Override
    public UmlDocument adapter(UmlSupportSource umlSupportSource) {
        Element source = umlSupportSource.getSource();
        UmlDocument document = new UmlDocument();
        initUmlDocument(source, document);
        return document;
    }

    public void initUmlDocument(Element source, UmlDocument document) {
        source.getContent().forEach(f -> {
            Element e = (Element) f;
            String name = e.getName();
            if ("nodes".equals(name)) {
                document.setNodeList(getNodeList(e));
            } else if ("edges".equals(name)) {
                document.setEdgeList(getEdgeList(e));
            } else if ("OriginalElement".equals(name)) {
                document.setTitle(e.getValue());
            }
        });
    }

    private List<Node> getNodeList(Element e) {
        List<Node> nodeList = new ArrayList<>();
        e.getContent().forEach(n -> {
            Element node_e = ((Element) n);
            List<Attribute> attributeList = node_e.getAttributes();
            Node node = new Node();
            node.setId(Md5Utils.generateMd5(node_e.getValue().getBytes()));
            node.setName(StringUtils.substringAfterLast(node_e.getValue(), "."));
            ClassType classType=UmlAdapterAction.nodeTypeMap.get(node.getId());
            //填充节点颜色 接口颜色：204,255,204 类颜色：204,229,255
            node.setColor(classType== ClassType.INTERFACE||classType==ClassType.ANNOTATIONTYPE ? "204,255,204" : "204,229,255 ");
            try {
                //整体右移和下移100px
                node.setX(attributeList.get(0).getDoubleValue() + PADDING);
                node.setY(attributeList.get(1).getDoubleValue() + PADDING);
                node.setWidth(Double.valueOf(getStrWidth(node.getName())) + 40);
                node.setHeight(Double.valueOf(30));
            } catch (DataConversionException e1) {
                e1.printStackTrace();
            }
            nodeMap.put(node.getId(), node);
            nodeList.add(node);
        });
        return nodeList;
    }

    private int getStrWidth(String str) {
        Font f = new Font("Arial", Font.PLAIN, 13);
        JComponent j = new JLabel();
        FontMetrics fm = j.getFontMetrics(f);
        int width = fm.stringWidth(str);//字符串宽度
        return width+(str.length()/10);
    }

    private List<Edge> getEdgeList(Element e) {
        List<Edge> edgeList = new ArrayList<>();
        e.getContent().forEach(n -> {
            Element edge_e = ((Element) n);
            List<Attribute> attributeList = edge_e.getAttributes();
            Edge edge = new Edge();
            edge.setSource(Md5Utils.generateMd5(attributeList.get(0).getValue().getBytes()));
            edge.setTarget(Md5Utils.generateMd5(attributeList.get(1).getValue().getBytes()));
            edge.setSourceHeadType(HeadType.NORMAL);
            edge.setTargetHeadType(HeadType.TRIANGLE_HOLLOW);
            edge.setLineType(getLineType(Md5Utils.generateMd5(attributeList.get(0).getValue().getBytes()), Md5Utils.generateMd5(attributeList.get(1).getValue().getBytes())));
            List<Point> pointList = new ArrayList<>();
            edge_e.getContent().forEach(p -> {
                Element point_e = (Element) p;
                Point point = new Point();
                List<Attribute> ponitAttributeList = point_e.getAttributes();
                try {
                    point.setX(ponitAttributeList.get(0).getDoubleValue() + PADDING);
                    point.setY(ponitAttributeList.get(1).getDoubleValue() + PADDING);
                } catch (DataConversionException e1) {
                    e1.printStackTrace();
                }
                pointList.add(point);
            });
            //umlSupport中首尾两个节点是相对source和target的相对坐标，需要处理下
            pointList.get(0).setX(nodeMap.get(edge.getSource()).getX() + pointList.get(0).getX() + (nodeMap.get(edge.getSource()).getWidth() / 2) - PADDING);
            pointList.get(0).setY(nodeMap.get(edge.getSource()).getY() + pointList.get(0).getY() + (nodeMap.get(edge.getSource()).getHeight() / 2) - PADDING);
            pointList.get(pointList.size() - 1).setX(nodeMap.get(edge.getTarget()).getX() + pointList.get(pointList.size() - 1).getX() + ((nodeMap.get(edge.getTarget()).getWidth() / 2)) - PADDING);
            pointList.get(pointList.size() - 1).setY(nodeMap.get(edge.getTarget()).getY() + pointList.get(pointList.size() - 1).getY() + (nodeMap.get(edge.getTarget()).getHeight() / 2) - PADDING);
            if (pointList.size() == 4) {
                pointList.get(1).setX(pointList.get(1).getX() - 3);
                pointList.get(2).setX(pointList.get(2).getX() - 4);
            }
            edge.setPointList(pointList);
            edgeList.add(edge);
        });
        return edgeList;
    }

    private LineType getLineType(String source, String target) {
        ClassType sourceType = UmlAdapterAction.nodeTypeMap.get(source);
        ClassType targetType = UmlAdapterAction.nodeTypeMap.get(target);
        if ((sourceType == ClassType.CLASS||sourceType== ClassType.ABSTRACT) && targetType == ClassType.INTERFACE) {
            return LineType.DOTTED;
        } else {
            return LineType.SOLID;
        }
    }

}
